package oop.ex2.main;

import java.util.*;
import java.util.regex.Matcher;

import java.io.*;

import oop.ex2.enums.IfWhileTypes;
import oop.ex2.enums.ValueTypes;
import oop.ex2.enums.VariableTypes;
import oop.ex2.types.*;

public class CheckMethod {
	//the list of all methods
	private HashMap<String,Method> methods;
	//the list of all global variables
	private HashMap<String, Variable> globalVariables;
	//a list of local variables in the specific method
	private HashMap<String,Variable> localVariables=new HashMap<String, Variable>();
	//the name of the method that is beeing checked
	private String methodName;
	
	/**
	 * this methods checks if the value of a variable is valid
	 * if it is only the assignment or initiation
	 * @param line the variable line
	 * @param matcher the matcher of the variable
	 * @param variables the list of variables we are checking
	 * @return true if the value is valid false if the value is not valid
	 * @throws IllegalCodeException 
	 */
	private boolean checkValue(String line,Matcher matcher,HashMap<String, Variable> variables) throws IllegalCodeException{
		if(variables.containsKey(matcher.group(2))){
			if(!matcher.group(1).equals("")){
				throw new IllegalCodeException();

			}
			else{
				return valueValidation(matcher, variables);
			}
		}
		else{
			if(matcher.group(1).equals("")){
				throw new IllegalCodeException();
			}
			else{
				if(valueValidation(matcher, variables)){
					localVariables.put(matcher.group(1).trim(), new Variable(matcher.group(2), matcher.group(3), matcher.group(1)));
					return true;
				}
			}
			
		}
		return false;
	}

	/**
	 * 
	 * @param matcher
	 * @return
	 */
	private boolean valueValidation(Matcher matcher, HashMap<String, Variable> variables) {
		if(!variables.isEmpty()){
			String returnType=variables.get(matcher.group(2).trim()).getType();
			String value=matcher.group(3).replaceAll("=", "");
			Matcher returnMatcher=ValueTypes.valueOf(returnType.toUpperCase()).getPattern().matcher(value.trim());
			if(returnMatcher.matches()){
				return true;
			}
			return false;
		}
		return true;
	}
	/**
	 * 
	 * @param val
	 * @param map
	 * @return
	 */
	private boolean existInList(String val , HashMap<String, Variable> map){
		for(String key:map.keySet()){
			if(key.equals(val))
				return true;
		}
		return false;
	}
	
	
	/**
	 * a constructor for check method class
	 * @param methods
	 * @param globalVariables
	 * @param methodName
	 */
	public CheckMethod(HashMap<String,Method> methods, HashMap<String, Variable> globalVariables, String methodName ){
		this.methods=methods;
		this.globalVariables=globalVariables;
		this.methodName=methodName;
	}
	
	/**
	 * a method that check validity of a method
	 * @throws IllegalCodeException 
	 */
	public void check() throws IllegalCodeException{
		Method method=methods.get(methodName);
		Iterator<String> methodLines=method.getLines().iterator();
		
		
		while(methodLines.hasNext()){
			String line=methodLines.next();
			
			//checks if the line is a variable declaration
			for(VariableTypes type:VariableTypes.values()){
				Matcher matcher=type.getPattern().matcher(line);
				
				if(matcher.find()){
					
					if(!checkValue(line, matcher, globalVariables) && !checkValue(line, matcher, localVariables)){
						String reType = matcher.group(1);
						String value=matcher.group(3).replaceAll("=", "").substring(0, matcher.group(3).indexOf("(")).trim();
						
						//go over methods
						for(String key:methods.keySet()){
							Method currentMethod=methods.get(key);
							
							if(currentMethod.getName().equals(value)){
								if(currentMethod.getReturnType().equals(reType)){
									break;
								}
								else{
									throw new IllegalCodeException();
								}
							}
						}
						
						
						//go over global variables
						if(existInList(value, globalVariables)){
							Variable CurrentVariable=globalVariables.get(value);
							if(CurrentVariable.getType().equals(reType)){
								break;
							}
							
						}
						
						//go over local variables
						if(existInList(value, localVariables)){
							Variable CurrentVariable=localVariables.get(value);
							if(CurrentVariable.getType().equals(reType)){
								break;
							}
						}
						
						//go over arguments
						if(existInList(value, method.getArguments())){
							Variable CurrentVariable=method.getArguments().get(value);
							if(CurrentVariable.getType().equals(reType)){
								break;
							}
							
						}
					}
					System.out.println("("+matcher.group(3) + ")");
				}
			}
			
			
			Matcher ifWhileMatcher=IfWhileTypes.IF.getPattern().matcher(line);
			if(ifWhileMatcher.matches()){
				String condition=ifWhileMatcher.group(2);
				Matcher boolMatcher=ValueTypes.BOOLEAN.getPattern().matcher(condition);
				
				if(boolMatcher.matches()){
					break;
				}
				
				if(existInList(condition, globalVariables)){
					if(!globalVariables.get(condition).getType().equals("boolean")){
						throw new IllegalCodeException();
					}
				}
				if(existInList(condition, localVariables)){
					if(!localVariables.get(condition).getType().equals("boolean")){
						throw new IllegalCodeException();
					}
				}
				if(existInList(condition, method.getArguments())){
					if(!method.getArguments().get(condition).getType().equals("boolean")){
						throw new IllegalCodeException();
					}
				}
				
				String trimCondition=condition.substring(0, condition.indexOf("("));
				
				if(methods.containsKey(trimCondition)){
					if(!methods.get(trimCondition).getReturnType().equals("boolean")){
						throw new IllegalCodeException();
					}
				}
				
			}
			
			throw new IllegalCodeException();
		}
	}
}
